package com.studerb.nntp;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.net.nntp.NNTPCommand;
import org.apache.commons.net.nntp.NNTPReply;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.log4j.Logger;
import org.bushe.swing.event.EventService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;

import com.studerb.events.ConnectionPoolEvent;
import com.studerb.model.Server;
import com.studerb.service.interfaces.IConnectionPoolService;
import com.studerb.service.interfaces.IServerService;

/**
 * Very simple NntpConnectionPoolFactory implementation uses the single master server from which to create
 * {@link com.studerb.nntp.NntpConnection NntpConnection} objects
 */
public class SingleServerObjectPoolFactory implements PoolableObjectFactory, ApplicationContextAware {
    private final Logger logger = Logger.getLogger(SingleServerObjectPoolFactory.class);
    private AbstractApplicationContext appContext;
    @Autowired IServerService serverService;
    @Autowired EventService eventService;
    @Autowired protected IConnectionPoolService singleServerConnectionPoolService;

    @Override public void activateObject(Object arg0) throws Exception {
        NntpConnection conn = (NntpConnection)arg0;
        logger.debug("Activating nntp connection: " + conn.toString());
        if( !conn.isConnected()){
            conn.connect();
        }
    }

    @Override public void destroyObject(Object arg0) throws Exception {
        NntpConnection conn = (NntpConnection)arg0;
        logger.debug("Destroying nntp connection: " + conn.toString());
        conn.disconnect();
        ConnectionPoolEvent event = new ConnectionPoolEvent(conn, ConnectionPoolEvent.EventType.DESTROYED);
        eventService.publish(event);
    }

    @Override public Object makeObject() throws Exception {
        logger.debug("Making new nntp connection.");
        Server master = serverService.getMasterServer();
        if(master == null){
            throw new Exception("Cannot Make Object: No Master Server");
        }
        NntpConnection conn = (NntpConnection)appContext.getBean("nntpConnection");
        conn.setServer(master);
        ConnectionPoolEvent event = new ConnectionPoolEvent(conn, ConnectionPoolEvent.EventType.CREATED);
        eventService.publish(event);
        return conn;
    }

    @Override public void passivateObject(Object arg0) throws Exception {
        // TODO figure out a way to flush the reader/write of nntpConnection object
        /*
         * log.debug("Passivating connection."); NntpConnection conn = (NntpConnection)arg0; Reader reader =
         * conn.getReader(); while(true){ reader.read }
         */
    }

    @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        appContext = (AbstractApplicationContext)applicationContext;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.commons.pool.PoolableObjectFactory#validateObject(java.lang.Object) Validate the NNTP Connection
     * by sending a 'MODE READER' command
     */
    @Override public boolean validateObject(Object arg0) {
        NntpConnection conn = (NntpConnection)arg0;
        logger.debug("Validating nntp connection: " + conn.toString());
        try{
            int reply = conn.sendCommand(NNTPCommand.MODE, "READER");
            if(NNTPReply.isNegativeTransient(conn.getReplyCode())){
                singleServerConnectionPoolService.reportServerTransientError(conn.getServer());
            }
            return NNTPReply.isPositiveCompletion(reply) ? true : false;
        }
        catch(Exception e){
            // check for possible authentication problems
            logger.debug("Validating Exception: " + ExceptionUtils.getRootCauseMessage(e));
            return false;
        }
    }
}
